/*
 * Copyright 2011 Guidebee Pty Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 */

//--------------------------------- PACKAGE ------------------------------------
package com.mapdigit.practices.compositeui;

//--------------------------------- IMPORTS ------------------------------------
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.mapdigit.practices.compositeui.collections.ManagedObjectCollection;
import com.mapdigit.practices.compositeui.eventbroker.CancelEventArgs;
import com.mapdigit.practices.compositeui.eventbroker.CancelEventHandler;
import com.mapdigit.practices.compositeui.eventbroker.Event;
import com.mapdigit.practices.compositeui.eventbroker.EventArgs;
import com.mapdigit.practices.compositeui.eventbroker.EventHandler;
import com.mapdigit.practices.compositeui.smartparts.Workspace;

//[------------------------------ MAIN CLASS ----------------------------------]
////////////////////////////////////////////////////////////////////////////////
//--------------------------------- REVISIONS ----------------------------------
// Date       Name                 Tracking #         Description
// --------   -------------------  -------------      --------------------------
// 12AUG2011  James Shen                 	      Code review
////////////////////////////////////////////////////////////////////////////////
/**
 * Defines the work item into which smart parts run.
 * <P>
 * <hr>
 * <hr><b>&copy; Copyright 2011 Guidebee Pty Ltd. All Rights Reserved.</b>
 * @version     1.00, 12/08/11
 * @author      Guidebee Pty Ltd.
 */
public class WorkItem {

    /**
     * All Items included in the work item.
     */
    public  ManagedObjectCollection<Object> Items;
    
    /**
     * All child work items.
     */
    public ManagedObjectCollection<WorkItem> WorkItems;
    
    /**
     * All child work items.
     */
    public ManagedObjectCollection<Workspace> WorkSpaces;
    
    /**
     * All services included in the work item.
     */
    public ManagedObjectCollection<Object> Services;
    
    /**
     * Occurs when the WorkItem is activated.
     */
    @Event
    public EventHandler Activated;
    
    /**
     * Occurs when the WorkItem is activating.
     */
    @Event
    public CancelEventHandler Activating;
    
    /**
     * Occurs when the WorkItem is deactivated.
     */
    @Event
    public EventHandler Deactivated;
    
    /**
     * Occurs when the WorkItem is deactivated.
     */
    @Event
    public CancelEventHandler Deactivating;
    
    /**
     * Occurs when the WorkItem is disposed.
     */
    @Event
    public EventHandler Disposed;
    
    /**
     * Occurs when the WorkItem is initialized.
     */
    @Event
    public EventHandler Initialized;
    
    /**
     *  Occurs when the Run method is executed.
     */
    @Event
    public EventHandler RunStarted;
    
    /**
     * Occurs when the WorkItem is terminated.
     */
    @Event
    public EventHandler Terminated;
    
    /**
     * Occurs before a WorkItem is terminated.
     */
    @Event
    public EventHandler Terminating;
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Initializes a new instance of the WorkItem class.
     */
    public WorkItem() {
        status = WorkItemStatus.Inactive;
        initializeCollectionFacades();
    }
    
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Gets the root WorkItem (the one at the top of the hierarchy).
     * @return the root WorkItem
     */
    public WorkItem getRootWorkItem() {
        WorkItem result = this;

        while (result.parent != null) {
            result = result.parent;
        }
        return result;
    }
    
    
    public WorkItem getMatchedWorkItem(Class<?> type){
    	Class<?> workItemType=this.getClass();
    	Package packageType=workItemType.getPackage();
    	String packageName=packageType.getName();
    	List<Class<?>>matchTyped=new ArrayList<Class<?>>();
    	String tobeCheckedPackageName=type.getPackage().getName();
    	if(tobeCheckedPackageName.startsWith(packageName)){
    		matchTyped.add(workItemType);
    		Enumeration<WorkItem> emulator=WorkItems.getEmulatation();
    		while(emulator.hasMoreElements()){
    			workItemType=emulator.getClass();
    			packageType=workItemType.getPackage();
    			packageName=packageType.getName();
    			if(tobeCheckedPackageName.startsWith(packageName)){
    				matchTyped.add(workItemType);
    			}
    		}
    		
    	}
    	if(matchTyped.size()>0){
    		int maxLength=-1;
    		Class<?> foundType=null;
    		for(Class<?> matchedType:matchTyped){
    			if((matchedType.getPackage().getName()).length()>maxLength){
    				maxLength=(matchedType.getPackage().getName()).length();
    				foundType=matchedType;
    			}
    		}
    		if(foundType!=null){
    			return (WorkItem)injector.getInstance(foundType);
    		}else{
    			return this;
    		}
    		
    	}else{
    		return this;
    	}
    	
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Runs the work item by calling the OnRunStarted method, 
     * which will raise theRunStarted event and eventually 
     * the custom starting business logic a derived class placed on it.
     */
    public void Run() {
        onRunStarted();
    }

    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Gets the current WorkItemStatus of the WorkItem.
     * @return current WorkItemStatus of the WorkItem.
     */
    public WorkItemStatus getStatus() {
        return status;
    }
   
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Activates the WorkItem.
     */
    public void activate() {
        changeStatus(WorkItemStatus.Active);
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Deactivates the WorkItem.
     */
    public void deactivate() {
        changeStatus(WorkItemStatus.Inactive);
    }
    
    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Initializes the built-in services exposed by the WorkItem.
     */
    protected void initializeServices() {
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Activated event.
     */
    protected void onActivated() {
        if (Activated != null) {
            Activated.handleEvent(this, EventArgs.Empty);
        }

    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Activating event.
     * @param args event object.
     */
    protected void onActivating(CancelEventArgs args) {
        if (Activating != null) {
            Activating.handleEvent(this, args);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Deactivated event.
     */
    protected void onDeactivated() {
        if (this.Deactivated != null) {
            Deactivated.handleEvent(this, EventArgs.Empty);
        }


    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Deactivating event.
     * @param args event object.
     */
    protected void onDeactivating(CancelEventArgs args) {


        if (Deactivating != null) {
            Deactivating.handleEvent(this, args);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Disposed event.
     */
    protected void onDisposed() {
        if (Disposed != null) {
            Disposed.handleEvent(this, EventArgs.Empty);
        }


    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Initialized event.
     */
    protected void onInitialized() {
        if (Initialized != null) {
            Initialized.handleEvent(this, EventArgs.Empty);
        }


    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the RunStarted event. Derived classes can override this 
     * method to place custom business logic to execute when the Run
     * method is called on the WorkItem.
     */
    protected void onRunStarted() {
        if (RunStarted != null) {
            RunStarted.handleEvent(this, EventArgs.Empty);
        }

    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Terminated event.
     */
    protected void onTerminated() {
        if (this.Terminated != null) {
            Terminated.handleEvent(this, EventArgs.Empty);
        }


    }

    ////////////////////////////////////////////////////////////////////////////
    //--------------------------------- REVISIONS ------------------------------
    // Date       Name                 Tracking #         Description
    // ---------  -------------------  -------------      ----------------------
    // 12AUG2011  James Shen                 	          Code review
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Fires the Terminating event.
     */
    protected void onTerminating() {
        if (Terminating != null) {
            Terminating.handleEvent(this, EventArgs.Empty);
        }
    }


    private WorkItem parent;
    private WorkItemStatus status;
    
    @Inject
    private Injector injector;
    


    private IWorkItemActivationService getActivationService() {
        return Services.get(IWorkItemActivationService.class);
    }
    
    private void initializeCollectionFacades() {
        if (Services == null) {
            Services =  new ManagedObjectCollection<Object>(injector,this,
                    parent == null ? null : parent.Services);
        }
        
        if (WorkItems == null) {
            WorkItems =  new ManagedObjectCollection<WorkItem>(injector,this,
                    parent == null ? null : parent.WorkItems);
        }

        if (Items == null) {
            Items = new ManagedObjectCollection<Object>(injector,this,
                    parent == null ? null : parent.Items);
        }
    }
    
    private void throwIfWorkItemTerminated() {
        if (this.status == WorkItemStatus.Terminated) {
            throw new IllegalStateException("WorkItem has already been disposed or terminated.");
        }
    }

    private void fireIfActivating(WorkItemStatus newStatus, CancelEventArgs args) {
        if (newStatus == WorkItemStatus.Active) {
            onActivating(args);
        }
    }

    private void fireIfDeactivating(WorkItemStatus newStatus, CancelEventArgs args) {
        if (newStatus == WorkItemStatus.Inactive) {
            onDeactivating(args);
        }
    }

    private void fireIfTerminating(WorkItemStatus newStatus) {
        if (newStatus == WorkItemStatus.Terminated) {
            onTerminating();
        }
    }

    private void fireStatusEvents() {
        if (this.status == WorkItemStatus.Active) {
            onActivated();
        } else if (this.status == WorkItemStatus.Inactive) {
            onDeactivated();
        } else if (this.status == WorkItemStatus.Terminated) {
            onTerminated();
        }
    }

    private void changeStatus(WorkItemStatus newStatus) {
        throwIfWorkItemTerminated();

        WorkItemStatus oldStatus = status;

        if (oldStatus != newStatus) {
            CancelEventArgs args = new CancelEventArgs();
            fireIfActivating(newStatus, args);
            fireIfDeactivating(newStatus, args);
            fireIfTerminating(newStatus);
            if (args.getCancel() == false) {
                status = newStatus;
                if (getActivationService() != null) {
                    getActivationService().ChangeStatus(this);
                }
                fireStatusEvents();
            }
        }
    }

    
}
